package extensible;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.border.BevelBorder;

import mover.Mass;
import mover.Mover;
import mover.Spring;
import mover.MouseMass;



/**
 * Animates any number of ovals that start at random positions, moving in random
 * directions, and "bounce" elastically, i.e., as a perfect reflection, when
 * they hit the boundaries of the window.
 * 
 * 
 * @author Jonathan Su and Jeff Forte
 */
@SuppressWarnings("serial")
public class Canvas extends JComponent {
	// things to be animated
	protected List<Mover> myMovers;
	// additional state for adding and removing of shapes during animation
	private List<Mover> myMoversToRemove;
	private ListIterator<Mover> myIterator;
	private Mover myCurrent;
	// like a dice, generates a series of random numbers
	private Random myGenerator;
	private Mover currentMover;
	private boolean mousePredator;
	private boolean mouseInCanvas;
	private boolean wrapAround;
	public boolean addMouseSpring;
	public boolean removeMouseSpring;
	public Spring mouseSpring;
	private MouseMass myMouse;

	/**
	 * Create a Canvas with the given size.
	 * 
	 * @param size
	 *            width and height of canvas in pixels
	 */
	public Canvas(Dimension size) {
		// distinguish canvas from the rest of the window
		setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		// initialize animation state
		myMouse = new MouseMass(this);
		addMouseListener(myMouse);
		addMouseMotionListener(myMouse);
		myGenerator = new Random();
		myMovers = new ArrayList<Mover>();
		myMoversToRemove = new ArrayList<Mover>();
		mousePredator = false;
		mouseInCanvas = true;
		wrapAround = true;
		addMouseSpring = false;
		removeMouseSpring = false;
	}

	/**
	 * Remember given mover so it is painted on Canvas.
	 * 
	 * Note, movers are rendered in the order they are added.
	 * 
	 * @param m
	 *            mover to be added to those that are drawn
	 */
	public void add(Mover shape) {
		if (myIterator == null) {
			myMovers.add(shape);
		} else
			myIterator.add(shape);
	}

	/**
	 * Forget given mover so it is not painted on Canvas.
	 * 
	 * @param m
	 *            mover to be removed those that are drawn
	 */
	public void remove(Mover shape) {
		if (myIterator == null)
			myMovers.remove(shape);
		else if (myCurrent == shape)
			myIterator.remove();
		else
			myMoversToRemove.add(shape);
	}

//	public void remove(Flock flock) {
//		for (Mover mover : flock.getMyMovers()) {
//			remove(mover);
//		}
//	}

	/**
	 * Remove all shapes from the canvas.
	 */
	public void clear() {
		if (myIterator == null)
			myMovers.clear();
		else
			myMovers.addAll(myMovers);
	}

	/**
	 * Paint the contents of the canvas.
	 * 
	 * Never called by you directly, instead called by Java runtime when area of
	 * screen covered by this container needs to be displayed (i.e., creation,
	 * uncovering, change in status)
	 * 
	 * @param pen
	 *            used to paint shape on the screen
	 */
	public void paintComponent(Graphics pen) {
		// distinguish canvas from the rest of the window
		pen.setColor(Color.WHITE);
		pen.fillRect(0, 0, getSize().width, getSize().height);
		// paint shapes to be animated
		for (Mover m : myMovers) {
			m.paint(pen);
		}
	}

	/**
	 * Called by each step of timer, multiple times per second.
	 * 
	 * This should update the state of the animated shapes by just a little so
	 * they appear to move over time.
	 */
	public void update() {
		// animate each mover, taking care to add or remove new ones
		// appropriately
		//myIterator = myMovers.listIterator();
		if (addMouseSpring) {
			Mass otherMass = getClosestMass(myMouse.getCenter());
			mouseSpring = new Spring(otherMass, myMouse, 50, 0.005);
			myMovers.add(mouseSpring);
			addMouseSpring = false;
		} 
		if (removeMouseSpring) {
			myMoversToRemove.add(mouseSpring);
			removeMouseSpring = false;
		}
		//my code
		for (Mover current : myMoversToRemove) {
			myMovers.remove(current);
		}
		myMoversToRemove.clear();
		for (Mover current : myMovers) {
			current.update(this);
		}
//		while (myIterator.hasNext()) {
//			myCurrent = myIterator.next();
//			if (myMoversToRemove.contains(myCurrent)) {
//				myMoversToRemove.remove(myCurrent);
//				myIterator.remove();
//			} else {
//				myCurrent.update(this);
//			}
//		}
//		myIterator = null;
//		// clear out updates made during animation
//		for (Mover current : myMoversToRemove) {
//			myMovers.remove(current);
//		}
//		myMoversToRemove.clear();
	}
	


	/**
	 * Returns a random value between min and max, inclusive
	 * 
	 * @param min
	 *            minimum possible value
	 * @param max
	 *            maximum possible value
	 */
	public int nextIntInRange(int min, int max) {
		return myGenerator.nextInt(max - min + 1) + min;
	}

	/**
	 * Returns a random value between min and max, inclusive that is guaranteed
	 * not to be zero
	 * 
	 * @param min
	 *            minimum possible value
	 * @param max
	 *            maximum possible value
	 */
	public int nextNonZeroIntInRange(int min, int max) {
		int result = 0;

		while (result == 0) {
			result = nextIntInRange(min, max);
		}

		return result;
	}

	public Mover checkForObjectInPosition(Point2D mouse) {
		for (Mover current : myMovers) {
			Point2D.Double myCenter = current.getCenter();
			Dimension mySize = current.getSize();
			if (mouse.distance(myCenter) < mySize.width / 2) {
				return current;
			}
		}

		return null;
	}

	public Mass getClosestMass(Point2D mouse) {
		Mass closest = null;
		double minDistance = Math.max(this.getHeight(), this.getWidth());
		for (Mover current : myMovers) {
			if (Mass.class.isInstance(current)) {
				double currentDistance = distance(mouse, current.getCenter());
				if (currentDistance <= minDistance) {
					minDistance = currentDistance;
					closest = (Mass) current;
				}
			}
		}
		return closest;
	}
	
	public double distance(Point2D p1, Point2D p2) {
		return Math.sqrt((p1.getX()-p2.getX())*(p1.getX()-p2.getX())+(p1.getY() - p2.getY())*(p1.getY()-p2.getY()));
	}
	
	public void setCurrentMover(Mover mover) {
		if(currentMover!=null)
		currentMover.setColor(Color.GREEN);
		mover.setColor(Color.RED);
		currentMover = mover;

	}

	public Mover getCurrentMover() {
		return currentMover;
	}

	public void setMouseAsPredator(boolean b) {
		mousePredator = b;
	}

	public boolean isMousePredator() {
		return mousePredator;
	}

	public void toggleMousePredator() {
		mousePredator = (!mousePredator);
	}
	
	public void toggleAddMuscle() {
		addMouseSpring = (!addMouseSpring);
	}

	public boolean isMouseInCanvas() {
		return mouseInCanvas;
	}

	public void setMouseInCanvas(boolean b) {
		mouseInCanvas = b;
	}

	public void toggleWrapAround() {
		wrapAround = !wrapAround;
	}

	public boolean wrapAround() {
		return wrapAround;
	}
}
